import U from "../../util/index.js";
import script from "../script.js";
const L = U.global.xsloader;

/*
 * Require-CSS RequireJS css! loader plugin
 * 0.1.8
 * Guy Bedford 2014
 * MIT
 */
L.define("css", function() {
	class Node {
		parent;
		children = {};
		mindex;
		_maxindex = -1;
		_minindex;
		doms = {}; //css index:dom
		id;
		constructor(id, mindex) {
			this.id = id;
			this.mindex = mindex;
		}

		addChild(node) {
			this.children[node.id] = node;
			node.parent = this;
		}
		getChild(id) {
			return this.children[id];
		}

		getChildren() {
			let values = [];
			for (let k in this.children) {
				values.push(this.children[k]);
			}

			values.sort((a, b) => {
				return a.mindex - b.mindex;
			});

			return values;
		}

		maxAnchorDom(after = true) {
			if (this._maxindex) {
				return after ? this.doms[this._maxindex]?.nextSibling : this.doms[this._maxindex];
			}
		}

		/**
		 * 将添加到返回节点之前
		 */
		findAnchor(cssIndex, dom,mthiz) {
			let href;
			if (dom) {
				this.doms[cssIndex] = dom;
				href = dom.getAttribute("href");
			}

			let anchorDom;

			if (this._maxindex == -1) {
				this._maxindex = cssIndex;
				this._minindex = cssIndex;

				function getOrderTreeNodes() {
					function ceil(n) {
						return Math.pow(10, parseInt(Math.log(n + 1) / Math.log(10) + 1));
					}

					function orderTreeNodes(nodes, target, upperKey) {
						let maxMindex = 0;
						for (let k in nodes) {
							let node = nodes[k];
							if (node.mindex > maxMindex) {
								maxMindex = node.mindex;
							}
						}

						let incIndex = ceil(maxMindex);
						for (let k in nodes) {
							let node = nodes[k];
							let key = upperKey + (incIndex + node.mindex);
							target[key] = {
								node,
								key,
							};
							orderTreeNodes(node.children, target, key + "-");
						}
					}

					let target = {};
					orderTreeNodes(rootNodes, target, "");
					let targetValues = [];
					for (let k in target) {
						targetValues.push(target[k]);
					}

					return targetValues;
				}

				//console.log("mthiz.src:", mthiz.src(), "href:", href);
				let targetValues = getOrderTreeNodes();
				targetValues.sort((a, b) => {
					if (a.key > b.key) {
						return -1;
					} else if (a.key < b.key) {
						return 1;
					} else {
						return 0;
					}
				});

				let rindex = -1;
				let anchor1, anchor2;
				for (let i = 0; i < targetValues.length; i++) {
					let node = targetValues[i].node;
					if (node === this) {
						rindex = i;
					} else {
						if (rindex == -1) {
							anchor1 = node.maxAnchorDom();
						} else if (!anchor2) {
							anchor2 = node.maxAnchorDom(false);
							if (anchor2) {
								break;
							}
						}
					}
				}

				anchorDom = anchor2 || anchor1;
			} else {
				if (cssIndex > this._maxindex) {
					anchorDom = this.maxAnchorDom();
					this._maxindex = cssIndex;
				} else {
					if (this._minindex > cssIndex) {
						this._minindex = cssIndex;
					}

					for (let i = cssIndex + 1; i < this._maxindex; i++) {
						if (this.doms[i]) {
							anchorDom = this.doms[i];
							break;
						}
					}
				}
			}

			if (!anchorDom) {
				anchorDom = L.script();
			}

			return anchorDom;
		}
	}

	let engine = window.navigator.userAgent.match(
		/Trident\/([^ ;]*)|AppleWebKit\/([^ ;]*)|Opera\/([^ ;]*)|rv\:([^ ;]*)(.*?)Gecko\/([^ ;]*)|MSIE\s([^ ;]*)|AndroidWebKit\/([^ ;]*)/
	) || 0;
	let useImportLoad = false;
	let useOnload = true;
	if (engine[1] || engine[7])
		useImportLoad = parseInt(engine[1]) < 6 || parseInt(engine[7]) <= 9;
	else if (engine[2] || engine[8] || 'WebkitAppearance' in document.documentElement.style)
		useOnload = false;
	else if (engine[4])
		useImportLoad = parseInt(engine[4]) < 18;

	const cssAPI = {};

	//id:Node
	const rootNodes = {};

	function buildAndGetNode(mthiz, mindex) {
		let id = mthiz.getId();
		let p = mthiz.invoker();

		let ids = [];
		ids.push(id);
		while (p && p.getId() != id) {
			ids.push(p.getId());
			p = p.invoker();
		}

		ids.reverse();
		let node; //最终为当前节点
		for (let id of ids) {
			if (!node) { //根节点
				if (!rootNodes[id]) {
					rootNodes[id] = new Node(id, mindex);
				}

				node = rootNodes[id];
			} else {
				let n = node.getChild(id);
				if (n) {
					node = n;
				} else {
					n = new Node(id, mindex);
					node.addChild(n);
					node = n;
				}
			}
		}

		return node;
	}

	function appendCssDom(dom, cssThiz, inverse) {
		if (cssThiz && inverse) {
			let mthiz = cssThiz.invoker();
			let mindex = cssThiz.getIndex();

			let node = buildAndGetNode(mthiz, mindex);
			let nextDom = node.findAnchor(mindex, dom,mthiz);

			script.head().insertBefore(dom, nextDom);
		} else {
			L.appendHeadDom(dom);
		}

	}

	let curStyle, curSheet;
	let createStyle = function(mthiz, inverse) {
		curStyle = document.createElement('style');
		appendCssDom(curStyle, mthiz, inverse);
		curSheet = curStyle.styleSheet || curStyle.sheet;
	};

	let ieCnt = 0;
	let ieLoads = [];
	let ieCurCallback;

	let createIeLoad = function(url, mthiz, inverse) {
		curSheet.addImport(url);
		curStyle.onload = function() {
			processIeLoad(mthiz, inverse);
		};

		ieCnt++;
		if (ieCnt == 31) {
			createStyle(mthiz, inverse);
			ieCnt = 0;
		}
	};

	let processIeLoad = function(mthiz, inverse) {
		ieCurCallback();
		let nextLoad = ieLoads.shift();
		if (!nextLoad) {
			ieCurCallback = null;
			return;
		}
		ieCurCallback = nextLoad[1];
		createIeLoad(nextLoad[0], mthiz, inverse);
	};

	let importLoad = function(url, callback, mthiz, inverse) {
		callback = callback || function() {};
		if (!curSheet || !curSheet.addImport)
			createStyle(mthiz, inverse);

		if (curSheet && curSheet.addImport) {
			if (ieCurCallback) {
				ieLoads.push([url, callback]);
			} else {
				createIeLoad(url, mthiz, inverse);
				ieCurCallback = callback;
			}
		} else {
			curStyle.textContent = '@import "' + url + '";';
			let loadInterval = setInterval(function() {
				try {
					curStyle.sheet.cssRules;
					clearInterval(loadInterval);
					callback();
				} catch (e) {
					console.warn(e);
				}
			}, 10);
		}
	};
	let linkLoad = function(url, callback, mthiz, inverse) {
		callback = callback || function() {};
		let link = document.createElement('link');
		link.type = 'text/css';
		link.rel = 'stylesheet';
		if (useOnload)
			link.onload = function() {
				link.onload = function() {};
				setTimeout(callback, 7);
			};
		else {
			let loadInterval = setInterval(function() {
				for (let i = 0; i < document.styleSheets.length; i++) {
					let sheet = document.styleSheets[i];
					if (sheet.href == link.href) {
						clearInterval(loadInterval);
						return callback();
					}
				}
			}, 10);
		}
		link.href = url;
		appendCssDom(link, mthiz, inverse);
	};
	cssAPI.pluginMain = function(cssId, onload, onerror, config) {
		let inverse = !(config.plugins.css && config.plugins.css.inverse === false); //默认逆向
		//			if(cssId.indexOf(".css") != cssId.length - 4) {
		//				cssId += ".css";
		//			}
		//		console.log("cssId="+cssId+",cssSrc="+this.invoker().getUrl(cssId, true)+",absUrl="+this.invoker().absUrl());
		(useImportLoad ? importLoad : linkLoad)(this.invoker().getUrl(cssId, true), onload, this, inverse);
	};
	cssAPI.getCacheKey = function(cssId) {
		//			if(cssId.indexOf(".css") != cssId.length - 4) {
		//				cssId += ".css";
		//			}
		let invoker = this.invoker();
		return invoker ? invoker.getUrl(cssId, true) : cssId;
	};
	cssAPI.loadCss = function(cssPath, callback) {
		(useImportLoad ? importLoad : linkLoad)(L.getUrl(cssPath), callback);
	};

	cssAPI.loadCsses = function() {
		let args = arguments;
		for (let i = 0; i < args.length; i++) {
			(useImportLoad ? importLoad : linkLoad)(L.getUrl(args[i]), null);
		}
	};
	return cssAPI;
});
